// Copyright 2023-2023 The jdh99 Authors. All rights reserved.
// 判断校验是否通过
// Authors: jdh99 <jdh821@163.com>

package besta

import (
	"bufio"
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"fmt"
	"github.com/btcsuite/btcutil/base58"
	"os"
	"runtime"
	"time"
)

var gKey []byte
var gPilotRunStartTime string
var gPilotRunExpireTime string

var gIsOK = false
var gExpire time.Time
var gExpireUtc int64

// 试运行剩余天数
var gDays = 0

// Load 模块载入
// key:密钥,注意密钥长度必须为16字节
// PilotRunStartTime 试运行开始时间.格式:"2024-07-23"
// PilotRunExpireTime 试运行结束时间.格式:"2025-10-01"
func Load(key []byte, PilotRunStartTime string, PilotRunExpireTime string) bool {
	if len(key) != 16 {
		return false
	}

	gKey = make([]byte, 16)
	for i := 0; i < 16; i++ {
		gKey[i] = key[i]
	}

	gPilotRunStartTime = PilotRunStartTime
	gPilotRunExpireTime = PilotRunExpireTime

	auth()
	go checkExpire()

	go func() {
		for {
			startTime := days(gPilotRunStartTime)
			if startTime < 0 {
				panic("Authentication failed, pilot run is end, please renew license!")
			}

			gDays = days(gPilotRunExpireTime)
			if IsPilotRunExpire() == false {
				time.Sleep(time.Hour)
				continue
			}

			if IsOK() == false {
				panic("Authentication failed, please renew license!")
			}
			time.Sleep(time.Hour)
		}
	}()

	return true
}

func auth() {
	number := getNumber()
	if number == "" {
		//fmt.Println("auth is fail!number is wrong!")
		return
	}

	// 解密
	decrypted := aesDecryptCBC(base58.Decode(number), gKey)
	if len(decrypted) <= 26 {
		fmt.Println("auth is fail!len is wrong!")
		return
	}
	code := string(decrypted[:12])
	expire := string(decrypted[12:20])
	nodeNum := string(decrypted[20:26])
	md5Check := decrypted[26:]

	// 判断MD5校验是否正确
	plaintext := []byte(code + expire + nodeNum)
	m := md5.New()
	m.Write(plaintext)
	md5Calc := m.Sum(nil)[10:]
	for i := 0; i < 6; i++ {
		if md5Check[i] != md5Calc[i] {
			fmt.Println("auth is fail!check is wrong!")
			return
		}
	}

	// 判断机器是否正确
	if code != Generate() {
		fmt.Println("auth is fail!machine is wrong!")
		return
	}

	// 判断是否到期
	layout := "20060102" // 时间戳字符串的格式
	t, err := time.Parse(layout, expire)
	if err != nil {
		fmt.Println("auth is fail!time is wrong!")
		return
	}
	utc := t.UTC()
	gExpireUtc = utc.Unix()
	gExpire = utc
	if time.Now().Unix() > gExpireUtc {
		fmt.Println("auth is fail!time is expire!", utc)
		return
	}

	gIsOK = true
}

func getNumber() string {
	fileHandler, err := os.Open("skylark.lic")
	if err != nil {
		return ""
	}

	fileReader := bufio.NewReader(fileHandler)
	oneLine, _, err := fileReader.ReadLine()
	if err != nil {
		return ""
	}
	return string(oneLine)
}

func aesDecryptCBC(encrypted []byte, key []byte) (decrypted []byte) {
	block, _ := aes.NewCipher(key)                              // 分组秘钥
	blockSize := block.BlockSize()                              // 获取秘钥块的长度
	blockMode := cipher.NewCBCDecrypter(block, key[:blockSize]) // 加密模式
	decrypted = make([]byte, len(encrypted))                    // 创建数组
	blockMode.CryptBlocks(decrypted, encrypted)                 // 解密
	decrypted = pkcs5UnPadding(decrypted)                       // 去除补全码
	return decrypted
}

func pkcs5UnPadding(origData []byte) []byte {
	length := len(origData)
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

func checkExpire() {
	for {
		time.Sleep(time.Second)

		if gIsOK == false {
			//_, _ = fmt.Scanln()
			runtime.Goexit()
		}

		if time.Now().Unix() > gExpireUtc {
			fmt.Println("auth is fail!time is expire!", gExpire)
			//_, _ = fmt.Scanln()
			runtime.Goexit()
		}
	}
}

// days 试运行已开始天数
func days(day string) int {
	// 解析日期字符串为时间对象
	layout := "2006-01-02"
	specifiedDate, err := time.Parse(layout, day)
	if err != nil {
		// 错误格式则认为过期
		return -1
	}

	// 获取当前时间
	now := time.Now()

	// 计算天数差
	d := now.Sub(specifiedDate).Hours() / 24
	return int(d)
}

// IsOK 是否认证通过
func IsOK() bool {
	return gIsOK
}

// IsPilotRunExpire 是否试运行到期
func IsPilotRunExpire() bool {
	if gDays <= 0 {
		return false
	}
	return true
}
